package ihm.panel;

import javax.imageio.ImageIO;
import javax.media.Buffer;
import javax.media.CannotRealizeException;
import javax.media.CaptureDeviceInfo;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.NoPlayerException;
import javax.media.Player;
import javax.media.control.FrameGrabbingControl;
import javax.media.format.VideoFormat;
import javax.media.util.BufferToImage;
import javax.swing.JPanel;

import tools.BMPUtils;
import tools.Resizer;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Permet de faire des captures d'écran sur un compossant swing (en utilisant JMF), à interval régulier.
 */
public class BMPCapture extends JPanel {

	private static final long serialVersionUID = 1L;

	public static Player player = null;
	public CaptureDeviceInfo di = null;
	public MediaLocator ml = null;
	public Buffer buf = null;
	public Image img = null;
	public VideoFormat vf = null;
	public BufferToImage btoi = null;

	private byte[] header;
	private byte[] data;

    private final Object lock = new Object();

	/**
	 * Constructeur.
	 */
	public BMPCapture(String url) {
		setLayout(new BorderLayout());
		setSize(320, 240);

		try {
			startPlayer(url);
		} catch (NoPlayerException e) {
			e.printStackTrace();
		} catch (CannotRealizeException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//capture(true);
	}
	
	public void startPlayer(String url) throws NoPlayerException, CannotRealizeException, IOException, InterruptedException {
		ml = new MediaLocator(url);
		player = Manager.createRealizedPlayer(ml);
		player.start();
		
		Thread.sleep(1000);
		Component comp;
		if ((comp = player.getVisualComponent()) != null)
			add(comp,BorderLayout.CENTER);
		Thread.sleep(3000);
	}
	
	/**
	 * Ferme la fenêtre proprement.
	 */
	public void stopPlayer() {
		player.close();
		player.deallocate();
	}
	
	/**
	 * Lance une tache périodique qui capture une image.
	 * @param fps
	 * @param delay en ms
	 * @param time en ms
	 */
	@Deprecated
	public void capture(int fps, int delay, final int time) {
		final long intervals = 1000/fps;
		TimerTask timerTask = new TimerTask() {
			@Override
			public void run() {
				FrameGrabbingControl fgc = (FrameGrabbingControl) player.getControl("javax.media.control.FrameGrabbingControl");
				buf = fgc.grabFrame();
				btoi = new BufferToImage((VideoFormat) buf.getFormat());
				img = btoi.createImage(buf);

				try {
					byte[] bmp = saveBMP_mem(img);
					header = BMPUtils.getHeader(bmp);
					data = BMPUtils.getData(header.length, bmp);
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		};

		Timer timer = new Timer();
		timer.scheduleAtFixedRate(timerTask, delay, intervals);
	}
	
	/**
	 * Enregistre une image.
	 * @param grab_header
	 */
	public void capture(boolean grab_header) {
		synchronized (lock) {
			byte[] bmp;
			FrameGrabbingControl fgc = (FrameGrabbingControl) player.getControl("javax.media.control.FrameGrabbingControl");
			buf = fgc.grabFrame();
			btoi = new BufferToImage((VideoFormat) buf.getFormat());
			img = btoi.createImage(buf);

			try {
				bmp = saveBMP_mem(img);
				if(grab_header == true)
					header = BMPUtils.getHeader(bmp);
				data = BMPUtils.getData(header.length, bmp);
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}
	
	/**
	 * Sauvegarde une image au format BMP.
	 * @param img
	 * @param s
	 * @throws IOException
	 */
	@Deprecated
	public static void saveBMP_disk(Image img, String s) throws IOException {
		BufferedImage bi = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_3BYTE_BGR);
		Graphics2D g2 = bi.createGraphics();
		File outputfile = new File(s);
		
		g2.drawImage(img, null, null);

		bi = Resizer.PROGRESSIVE_BILINEAR.resize(bi, 320, 240);
		ImageIO.write(bi, "BMP", outputfile);
	}

	/**
	 * Sauvegarde une image sous forme d'un tableau de byte. (BMP)
	 * @param img
	 * @throws IOException
	 */
	public static byte[] saveBMP_mem(Image img) throws IOException {
		BufferedImage bi = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_3BYTE_BGR);
		Graphics2D g2 = bi.createGraphics();
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		byte[] imageInByte = null;
		
		g2.drawImage(img, null, null);

		bi = Resizer.PROGRESSIVE_BILINEAR.resize(bi, 320, 240);

		ImageIO.write(bi, "bmp", baos);
		baos.flush();
		imageInByte = baos.toByteArray();
		baos.close();

		return imageInByte;
	}
	
	public byte[] getHeader() { return header; }
	public void setHeader(byte[] header) { this.header = header; }

	public byte[] getData() { return data; }
	public void setData(byte[] data) { this.data = data; }

	public static void main(String[] args) {
		Frame f = new Frame("Capture window");
		final BMPCapture cf = new BMPCapture("vfw://0");

		f.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				cf.stopPlayer();
				System.exit(0);
			}
		});

		f.add("Center", cf);
		f.pack();
		f.setSize(new Dimension(320, 310));
		f.setVisible(true);
	}

}
